/* Copyright (c) [2025] [zhi1208@126.com]
 * SPDX-License-Identifier: MIT
 */

/*********************
 *      INCLUDES
 *********************/
#include "lvgl.h"
#include "hmi_pg_navigation.h"
#include "hmi_pg_def.h"
#include "hmi_pg_manager.h"

/*********************
 *      DEFINES
 *********************/
#define HMI_NAV_GESTURE_THRESH LV_DPX(40)

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void dect_zone_ev_cb(lv_event_t *e);

/**********************
 *  STATIC VARIABLES
 **********************/
static lv_ll_t pg_stack;

static lv_obj_t *left_dect_zone;
static lv_obj_t *right_dect_zone;
static lv_obj_t *bottom_dect_zone;

static hmi_gesture_cb_t swipe_left_right_cb;
static hmi_gesture_cb_t swipe_up_cb;

static lv_ll_t push_start_cb_list;
static lv_ll_t push_cplt_cb_list;
static lv_ll_t pop_cplt_cb_list;
static lv_ll_t back_home_cplt_cb_list;
static lv_ll_t rebuild_all_pg_cplt_cb_list;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/
void hmi_pg_navigation_init()
{
    lv_ll_init(&pg_stack, sizeof(hmi_pg_t *));

    lv_ll_init(&push_start_cb_list, sizeof(hmi_stack_ops_cb_t));
    lv_ll_init(&push_cplt_cb_list, sizeof(hmi_stack_ops_cb_t));
    lv_ll_init(&pop_cplt_cb_list, sizeof(hmi_stack_ops_cb_t));
    lv_ll_init(&back_home_cplt_cb_list, sizeof(hmi_stack_ops_cb_t));
    lv_ll_init(&rebuild_all_pg_cplt_cb_list, sizeof(hmi_stack_ops_cb_t));

    hmi_set_swipe_left_right_cb(hmi_pg_pop);
    hmi_set_swipe_up_cb(hmi_back_home);

    left_dect_zone = lv_obj_create(lv_layer_sys());
    lv_obj_remove_style_all(left_dect_zone);
    lv_obj_set_size(left_dect_zone, lv_pct(5), lv_pct(90));
    lv_obj_align(left_dect_zone, LV_ALIGN_LEFT_MID, 0, 0);
    lv_obj_add_event_cb(left_dect_zone, dect_zone_ev_cb, LV_EVENT_ALL, &swipe_left_right_cb);

    right_dect_zone = lv_obj_create(lv_layer_sys());
    lv_obj_remove_style_all(right_dect_zone);
    lv_obj_set_size(right_dect_zone, lv_pct(5), lv_pct(90));
    lv_obj_align(right_dect_zone, LV_ALIGN_RIGHT_MID, 0, 0);
    lv_obj_add_event_cb(right_dect_zone, dect_zone_ev_cb, LV_EVENT_ALL, &swipe_left_right_cb);

    bottom_dect_zone = lv_obj_create(lv_layer_sys());
    lv_obj_remove_style_all(bottom_dect_zone);
    lv_obj_set_size(bottom_dect_zone, lv_pct(90), lv_pct(5));
    lv_obj_align(bottom_dect_zone, LV_ALIGN_BOTTOM_MID, 0, 0);
    lv_obj_add_event_cb(bottom_dect_zone, dect_zone_ev_cb, LV_EVENT_ALL, &swipe_up_cb);
}

void hmi_pg_push(hmi_pg_t *pg)
{
    LV_ASSERT_NULL(pg);

    hmi_stack_ops_cb_t *cb = lv_ll_get_head(&push_start_cb_list);
    while (cb)
    {
        (*cb)();
        cb = lv_ll_get_next(&push_start_cb_list, cb);
    }

    hmi_pg_t **pg_node = lv_ll_get_tail(&pg_stack);
    if (pg_node && (*pg_node == pg))
    {
        LV_LOG_WARN("当前页面已经是页面【%s】，无法入栈！", pg->name);
        return;
    }

    if (!hmi_pg_is_registered(pg))
    {
        if (!pg->create)
        {
            LV_LOG_WARN("页面【%s】的创建函数指针为空！", pg->name);
            return;
        }
        pg->create();
        hmi_register_pg(pg);
    }

    pg_node = lv_ll_ins_tail(&pg_stack);
    if (!pg_node)
    {
        LV_LOG_WARN("分配内存失败，无法压入页面【%s】！", pg->name);
        return;
    }
    *pg_node = pg;

    if (!pg->open)
    {

        LV_LOG_WARN("页面【%s】的创建函数指针为空！", pg->name);
        return;
    }
    pg->open();

    cb = lv_ll_get_head(&push_cplt_cb_list);
    while (cb)
    {
        (*cb)();
        cb = lv_ll_get_next(&push_cplt_cb_list, cb);
    }
}

void hmi_pg_pop()
{
    if (lv_ll_get_len(&pg_stack) <= 1)
    {
        LV_LOG_WARN("栈空，无法出栈！");
        return;
    }

    hmi_pg_t **pg = lv_ll_get_tail(&pg_stack);
    if (!(*pg)->close)
    {
        LV_LOG_WARN("页面【%s】的关闭函数指针为空！", (*pg)->name);
        return;
    }
    (*pg)->close();

    hmi_pg_t **prev_pg = lv_ll_get_prev(&pg_stack, pg);
    if ((*prev_pg)->open)
        (*prev_pg)->open();

    if ((*pg)->del_on_pop && (*pg)->del)
    {
        (*pg)->del();
        hmi_log_out_pg(*pg);
    }

    lv_ll_remove(&pg_stack, pg);
    lv_free(pg);

    hmi_stack_ops_cb_t *cb = lv_ll_get_head(&pop_cplt_cb_list);
    while (cb)
    {
        (*cb)();
        cb = lv_ll_get_next(&pop_cplt_cb_list, cb);
    }
}

void hmi_back_home()
{
    if (lv_ll_get_len(&pg_stack) <= 1)
    {
        LV_LOG_WARN("已是栈底！");
        return;
    }

    hmi_pg_t **pg_node = lv_ll_get_tail(&pg_stack);
    hmi_pg_t **pg_home = lv_ll_get_head(&pg_stack);
    while (pg_node != pg_home)
    {
        if ((*pg_node)->del_on_pop && (*pg_node)->del)
        {
            (*pg_node)->del;
            hmi_log_out_pg(*pg_node);
        }

        lv_ll_remove(&pg_stack, pg_node);
        hmi_pg_t **tmp = pg_node;
        pg_node = lv_ll_get_prev(&pg_stack, pg_node);
        lv_free(tmp);
    }

    (*pg_home)->open();

    hmi_stack_ops_cb_t *cb = lv_ll_get_head(&back_home_cplt_cb_list);
    while (cb)
    {
        (*cb)();
        cb = lv_ll_get_next(&back_home_cplt_cb_list, cb);
    }
}

void hmi_rebuil_all_pg()
{
    hmi_pg_t **pg = lv_ll_get_head(hmi_get_pg_list());
    while (pg)
    {
        if ((*pg)->create && (*pg)->del)
        {
            (*pg)->del();
            (*pg)->create();
        }
        pg = lv_ll_get_next(hmi_get_pg_list(), pg);
    }

    pg = lv_ll_get_tail(&pg_stack);
    if (pg)
        lv_scr_load((*pg)->root);

    hmi_stack_ops_cb_t *cb = lv_ll_get_head(&rebuild_all_pg_cplt_cb_list);
    while (cb)
    {
        (*cb)();
        cb = lv_ll_get_next(&rebuild_all_pg_cplt_cb_list, cb);
    }
}

hmi_pg_t *hmi_get_cur_page()
{
    hmi_pg_t **pg = lv_ll_get_tail(&pg_stack);
    return *pg;
}

void hmi_set_swipe_left_right_cb(hmi_gesture_cb_t cb)
{
    swipe_left_right_cb = cb;
    LV_LOG_INFO("左滑右滑手势回调函数设置为【%p】！", cb);
}

void hmi_set_swipe_up_cb(hmi_gesture_cb_t cb)
{
    swipe_up_cb = cb;
    LV_LOG_INFO("上滑手势回调函数设置为【%p】！", cb);
}

void hmi_add_push_start_cb(hmi_stack_ops_cb_t cb)
{
    hmi_stack_ops_cb_t *node = lv_ll_get_head(&push_start_cb_list);
    while (node)
    {
        if (*node == cb)
        {
            LV_LOG_WARN("回调函数【%p】已在【push_start_cb_list】中，无法重复添加！", cb);
            return;
        }
        node = lv_ll_get_next(&push_start_cb_list, node);
    }

    node = lv_ll_ins_tail(&push_start_cb_list);
    *node = cb;
}

void hmi_add_push_cplt_cb(hmi_stack_ops_cb_t cb)
{
    hmi_stack_ops_cb_t *node = lv_ll_get_head(&push_cplt_cb_list);
    while (node)
    {
        if (*node == cb)
        {
            LV_LOG_WARN("回调函数【%p】已在【push_cplt_cb_list】中，无法重复添加！", cb);
            return;
        }
        node = lv_ll_get_next(&push_cplt_cb_list, node);
    }

    node = lv_ll_ins_tail(&push_cplt_cb_list);
    *node = cb;
}

void hmi_add_pop_cplt_cb(hmi_stack_ops_cb_t cb)
{
    hmi_stack_ops_cb_t *node = lv_ll_get_head(&pop_cplt_cb_list);
    while (node)
    {
        if (*node == cb)
        {
            LV_LOG_WARN("回调函数【%p】已在【pop_cplt_cb_list】中，无法重复添加！", cb);
            return;
        }
        node = lv_ll_get_next(&pop_cplt_cb_list, node);
    }

    node = lv_ll_ins_tail(&pop_cplt_cb_list);
    *node = cb;
}

void hmi_add_back_home_cplt_cb(hmi_stack_ops_cb_t cb)
{
    hmi_stack_ops_cb_t *node = lv_ll_get_head(&back_home_cplt_cb_list);
    while (node)
    {
        if (*node == cb)
        {
            LV_LOG_WARN("回调函数【%p】已在【back_home_cplt_cb_list】中，无法重复添加！", cb);
            return;
        }
        node = lv_ll_get_next(&back_home_cplt_cb_list, node);
    }

    node = lv_ll_ins_tail(&back_home_cplt_cb_list);
    *node = cb;
}

void hmi_add_rebuild_all_pg_cplt_cb(hmi_stack_ops_cb_t cb)
{
    hmi_stack_ops_cb_t *node = lv_ll_get_head(&rebuild_all_pg_cplt_cb_list);
    while (node)
    {
        if (*node == cb)
        {
            LV_LOG_WARN("回调函数【%p】已在【rebuild_all_pg_cplt_cb_list】中，无法重复添加！", cb);
            return;
        }
        node = lv_ll_get_next(&rebuild_all_pg_cplt_cb_list, node);
    }

    node = lv_ll_ins_tail(&rebuild_all_pg_cplt_cb_list);
    *node = cb;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void dect_zone_ev_cb(lv_event_t *e)
{
    static lv_point_t start_point;

    hmi_gesture_cb_t *cb = lv_event_get_user_data(e);
    lv_obj_t *obj = lv_event_get_target(e);
    lv_event_code_t code = lv_event_get_code(e);

    switch (code)
    {
    case LV_EVENT_PRESSED:
        lv_indev_get_point(lv_indev_active(), &start_point);
        break;

    case LV_EVENT_RELEASED:
        lv_point_t point;
        lv_indev_get_point(lv_indev_active(), &point);
        if ((obj == left_dect_zone || obj == right_dect_zone) && (LV_ABS(point.x - start_point.x) > HMI_NAV_GESTURE_THRESH) ||
            (obj == bottom_dect_zone) && (LV_ABS(point.y - start_point.y) > HMI_NAV_GESTURE_THRESH))
        {
            if (*cb)
                (*cb)();
        }
        break;

    default:
        break;
    }
}
